package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.sun.xml.bind.v2.TODO;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

/**
 * 设备Service业务层处理
 *
 * @author ruoyi
 * @date 2025-02-26
 */
@Service
@Slf4j
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private IoTDAClient ioTDAClient;

    /**
     * 查询设备
     *
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id) {
        return getById(id);
    }

    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return save(device) ? 1 : 0;
    }

    /**
     * 修改设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device) {
        return updateById(device) ? 1 : 0;
    }

    /**
     * 批量删除设备
     *
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备信息
     *
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 从物联网平台同步产品列表
     */
    @Override
    public void syncProductList() {
        //设置请求参数
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        listProductsRequest.setLimit(50);
        //发起请求
        ListProductsResponse response = ioTDAClient.listProducts(listProductsRequest);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("物联网接口 - 查询产品，同步失败");
        }

        //存储到redis
        redisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT_LIST, JSONUtil.toJsonStr(response.getProducts()));
    }

    /**
     * 查询所有产品列表
     *
     * @return 产品列表
     */
    @Override
    public List<ProductVo> allProduct() {
        //查询redis获取产品列表
        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST);
        if (StringUtils.isEmpty(jsonStr)) {
            return Collections.emptyList();
        }

        return JSONUtil.toList(jsonStr, ProductVo.class);
    }


    /**
     * 注册设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //1.检查设备名称是否重复
        //1.1 构建LambdaQueryWrapper对象
        //1.2 设置设备名字筛选条件
        //1.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设备名称重复
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getDeviceName, deviceDto.getDeviceName());
        if (count(queryWrapper) > 0) {
            log.error("注册设备失败：设备名称 【{}】 重复", deviceDto.getDeviceName());
            throw new RuntimeException("注册设备失败：设备名称 【" + deviceDto.getDeviceName() + "】 重复");

        }

        //2.检查设备标识码是否重复
        //2.1 LambdaQueryWrapper对象清除上面的筛选条件（clear方法）
        queryWrapper.clear();
        //2.2 设置设备设备标识码筛选条件
        queryWrapper.eq(Device::getNodeId, deviceDto.getNodeId());
        //2.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设备标识码重复
        if (count(queryWrapper) > 0) {
            log.error("注册设备失败：设备标识码 【{}】 重复", deviceDto.getNodeId());
            throw new RuntimeException("注册设备失败：设备标识码 【" + deviceDto.getNodeId() + "】 重复");

        }
        //3.检查同一位置是否绑定了相同的产品
        //1.1 LambdaQueryWrapper对象清除上面的筛选条件（clear方法）
        queryWrapper.clear();
        //1.2 设置筛选条件：binding_location，location_type,physical_location_type,product_key查询有数据代表同一位置绑定了相同产品的设备
        queryWrapper.eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                .eq(Device::getLocationType, deviceDto.getLocationType())
                .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType())
                .eq(Device::getProductKey, deviceDto.getProductKey());
        //1.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设同一位置绑定了相同产品的设备
        if (count(queryWrapper) > 0) {
            log.error("注册设备失败：同一位置绑定了相同产品的设备");
            throw new RuntimeException("注册设备失败：同一位置绑定了相同产品的设备");
        }
        //4.项目华为IOT平台注册设备
        //4.1 请求参数对象AddDeviceRequest对象
        AddDeviceRequest addDeviceRequest = new AddDeviceRequest();
        //4.2 请求体参数对象AddDevice对象
        AddDevice addDevice = new AddDevice();
        //设备标识码 node_id
        //设备名称 device_name
        //产品id product_id
        addDevice.withNodeId(deviceDto.getNodeId())
                .withDeviceName(deviceDto.getDeviceName())
                .withProductId(deviceDto.getProductKey());
        //实例AuthInfo对象，封装secret（自己生成唯一值）
        AuthInfo authInfo = new AuthInfo();
        String secret = UUID.randomUUID().toString().replaceAll("-", "");
        authInfo.withSecret(secret);
        //AddDevice对象封装AuthInfo对象(withAuthInfo)
        addDevice.withAuthInfo(authInfo);

        //AddDeviceRequest对象封装AddDevice对象（withBody）
        addDeviceRequest.withBody(addDevice);
        //4.3 执行请求，获取响应结果AddDeviceResponse对象
        AddDeviceResponse addDeviceResponse = null;
        try {
            addDeviceResponse = ioTDAClient.addDevice(addDeviceRequest);
        } catch (Exception e) {
            log.error("物联网平台-注册设备失败：" + e.getMessage());
            throw new RuntimeException("注册设备失败：" + e.getMessage());
        }
        //4.4 判断响应状态为是否为200,如果不是抛出异常，提示“物联网平台-注册设备失败”
        if (addDeviceResponse.getHttpStatusCode() != 201) {
            log.error("物联网平台-注册设备失败");
            throw new RuntimeException("注册设备失败：物联网平台-注册设备失败");
        }
        //4.5 提示设备注册成功
        log.info("物联网平台-注册设备【{}】成功", deviceDto.getDeviceName());

        //5.本地保存设备数据到数据库中
        //5.1 将前端传递过来的deviceDto对象数据封装赋值给Device对象
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //5.2 补全数据
        //iot_id 从AddDeviceResponse对象中获取
        device.setIotId(addDeviceResponse.getDeviceId());
        //secret
        device.setSecret(secret);

        //判断physical_location_type为空给-1
        if (device.getPhysicalLocationType() == null) {
            device.setPhysicalLocationType(-1);
        }

        //5.3 插入数据库
        try {
            save(device);
        } catch (Exception e) {
            log.error("注册设备失败：" + e.getMessage());
            // 远程调用华为IOT删除设备(作业)，如果这里删除失败，人工接入，到IOT平台找到对应设备删除。
            DeleteDeviceRequest request = null;
            try {
                request = new DeleteDeviceRequest();
                request.withDeviceId(addDeviceResponse.getDeviceId());
            } catch (Exception ex) {
                log.error("设备删除失败，请人工接入，到IOT平台找到对应设备删除。");
                throw new RuntimeException("设备删除失败，请人工接入，到IOT平台找到对应设备删除。");
            }

            throw new RuntimeException("注册设备失败，原因：" + e.getMessage());
        }

    }

    /**
     * 查询设备详情
     *
     * @param iotId
     * @return
     */
    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        Device device = lambdaQuery().eq(Device::getIotId, iotId).one();
        if (ObjectUtil.isEmpty(device)){
            throw new BaseException("设备不存在");
        }
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);

        //发起请求到IOT平台查询设备详情
        ShowDeviceRequest showDeviceRequest = new ShowDeviceRequest();
        showDeviceRequest.withDeviceId(iotId);
        ShowDeviceResponse response = null;
        try {
            response =  ioTDAClient.showDevice(showDeviceRequest);
        } catch (Exception e) {
            log.error("物联网接口 - 查询设备详情，调用失败：" + e.getMessage());
            //throw new  BaseException(e.getMessage());
            return deviceDetailVo;
        }
        if (!(200 == response.getHttpStatusCode())){
            log.error("物联网接口 - 查询设备详情，调用失败");
            return deviceDetailVo;
        }
        deviceDetailVo.setDeviceStatus(response.getStatus());
        String activeTimeStr = response.getActiveTime();
        if (StringUtils.isNotEmpty(activeTimeStr)){
            LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
            //日期时区转换
            activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();
            deviceDetailVo.setActiveTime(activeTime);
        }

        return deviceDetailVo;
    }

    /**
     * 查询设备上报数据
     *
     * @param iotId
     * @return
     */
    @Override
    public AjaxResult queryServiceProperties(String iotId) {
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        ShowDeviceShadowResponse response = null;
        try {
            response = ioTDAClient.showDeviceShadow(request);
        } catch (Exception e) {
            log.error("查询设备上报数据失败：" + e.getMessage());
            throw new RuntimeException(e);
        }
        if (response.getHttpStatusCode() != 200){
            log.error("查询设备上报数据失败：");
            throw new RuntimeException("查询设备上报数据失败");
        }
        List<DeviceShadowData> shadow = response.getShadow();
        if(CollUtil.isEmpty(shadow)){
            List<Object> emptyList = Collections.emptyList();
            return AjaxResult.success(emptyList);
        }
        //返回数据
        JSONObject jsonObject =JSONUtil.parseObj(shadow.get(0).getReported().getProperties());
        List<Map<String,Object>> list = new ArrayList<>();
        //处理上报时间日期
        LocalDateTime activeTime =  LocalDateTimeUtil.parse(shadow.get(0).getReported().getEventTime(), "yyyyMMdd'T'HHmmss'Z'");
        //日期时区转换
        LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                .toLocalDateTime();


        jsonObject.forEach((key,value)->{
            Map<String,Object> map = new HashMap<>();
            map.put("functionId",key);
            map.put("value",value);
            map.put("eventTime",eventTime);
            list.add(map);
        });



        return AjaxResult.success(list);
    }

    /**
     * 修改设备信息
     *
     * @param device
     */
    @Override
    public void edit(Device device) {
        Device device1 = getById(device.getId());
        if (ObjectUtil.isEmpty(device1)){
            throw new BaseException("设备不存在");
        }

        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getBindingLocation, device.getBindingLocation())
                .eq(Device::getLocationType, device.getLocationType())
                .ne(Device::getId, device.getId())
                .eq(Device::getPhysicalLocationType, device.getPhysicalLocationType())
                .eq(Device::getProductKey, device.getProductKey());
        //1.3 执行查询，获取查询匹配的个数，个数大于0，说明已存在，抛出异常，提示设同一位置绑定了相同产品的设备
        if (count(queryWrapper) > 0) {
            log.error("修改设备失败：同一位置绑定了相同产品的设备");
            throw new RuntimeException("修改设备失败：同一位置绑定了相同产品的设备");
        }


        try {
            updateById(device);
        } catch (Exception e) {
            log.error("修改设备信息失败：");
            throw new RuntimeException(e);
        }

        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.withDeviceId(device1.getIotId());
        UpdateDevice body = new UpdateDevice();
        body.setDeviceName(device.getDeviceName());
        request.withBody(body);
        UpdateDeviceResponse response = null;
        try {
            response = ioTDAClient.updateDevice(request);
        } catch (Exception e) {
            log.error("修改设备信息失败：" + e.getMessage());
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        if (response.getHttpStatusCode() != 200){
            log.error("修改设备信息失败：");
            throw new RuntimeException("修改设备信息失败");
        }

    }

    /**
     * 根据iotId删除设备
     *
     * @param iotId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDeviceByiotId(String iotId) {
        //删除device表中对应的设备
        boolean b = lambdaUpdate().eq(Device::getIotId, iotId)
                .remove();
        if (!b){
            throw new RuntimeException("删除设备失败");
        }
        //查看iot中设备存不存在
        ShowDeviceRequest request1 = new ShowDeviceRequest();
        request1.withDeviceId(iotId);
        ShowDeviceResponse response1 = null;
        try {
            response1 = ioTDAClient.showDevice(request1);
        } catch (Exception e) {
            log.error("在iot中设备不存在");
            throw new RuntimeException("在iot中设备不存在");
        }
        if (response1.getHttpStatusCode() != 200){
            log.error("在iot中设备不存在");
        }

        //在iot中删除设备
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(iotId);
        DeleteDeviceResponse response = null;
        try {
            response = ioTDAClient.deleteDevice(request);
        } catch (Exception e) {
            log.error("在iot删除设备失败：" + e.getMessage());
            throw new RuntimeException(e);
        }

        //可以删除device_data表中对应设备的数据
    }

    /**
     * 查询产品详情
     *
     * @param productKey
     * @return
     */
    @Override
    public AjaxResult queryProduct(String productKey) {
        //参数校验
        if(StringUtils.isEmpty(productKey)){
            throw new BaseException("请输入正确的参数");
        }
        //调用华为云物联网接口
        ShowProductRequest request = new ShowProductRequest();
        request.withProductId(productKey);
        ShowProductResponse response = null;
        try {
             response = ioTDAClient.showProduct(request);
        }catch(Exception e){
            log.info("查询产品详情失败：" + e.getMessage());
            return AjaxResult.error("查询产品详情失败：" + e.getMessage());
        }
        List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();
        if (CollUtil.isEmpty(serviceCapabilities)){
            return AjaxResult.success(Collections.emptyList());

        }

        return AjaxResult.success(serviceCapabilities);
    }
}
